/*	--- Servlet进阶 --- 


一个【Web App】 = 【一个或多个Servlet】组成的, 【每个Servlet】通过【注解】说明【本Servlet能处理的路径】。

例如： */
@WebServlet(urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
	// ...
}

/*
上述【HelloServlet】能处理【/hello】这个【路径的请求】。

PS:  【早期的Servlet】需要在【web.xml】中配置【映射路径】，但【最新Servlet版本】只需要通过【注解】就可以完成【映射】。

=========================================================

因为【浏览器】发送【请求】的时候，还会有【请求方法】（HTTP Method）：
	即【GET、POST、PUT等】不同类型的【请求】。

因此，要处理【GET】请求，我们要【覆写doGet()方法】： */
@WebServlet(urlPatterns = "/helo")
public class HelloServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// 处理【GET】请求...
	}
}


/*
类似的，要处理【POST】请求，就需要覆写【doPost()方法】。
	即:
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		...
	}

如果没有覆写【doPost()方法】，那么【HelloServlet】能不能处理【POST /hello】请求呢？

我们查看一下【HttpServlet的doPost()方法】就一目了然了：
它会直接【返回405或400错误】。

因此，一个【Servlet】如果映射到【/hello】，那么【所有Request请求方法】都会由这个【Servlet处理】，至于能不能返回【200】成功响应，要看有没有覆写【对应的请求方法】。

------------------------

【一个Webapp】完全可以有【多个Servlet】，分别映射【不同的路径】。

例如： */
@WebServlet(urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
	// ...
}

@WebServlet(urlPatterns = "/signin")
public class SignInServlet extends HttpServlet {
	// ...
}

@WebServlet(urlPatterns = "/")
public class IndexServlet extends HttpServlet {
	// ...
}


/*
【浏览器】发出的【HTTP请求】总是由【Web Server】先接收，然后，根据【Servlet配置】的映射，【不同的访问路径】转发到【不同的Servlet】：

               ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐

               │            /hello    ┌───────────────┐│
                          ┌──────────>│ HelloServlet  │
               │          │           └───────────────┘│
┌───────┐    ┌──────────┐ │ /signin   ┌───────────────┐
│Browser│───>│Dispatcher│─┼──────────>│ SignInServlet ││
└───────┘    └──────────┘ │           └───────────────┘
               │          │ /         ┌───────────────┐│
                          └──────────>│ IndexServlet  │
               │                      └───────────────┘│
                    Web Server
               └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘


这种根据【路径转发】的功能: 我们一般称为【Dispatch】。

映射到 / 的IndexServlet比较特殊，它实际上会【接收所有未匹配的路径】，相当于【/*】，因为【Dispatcher的逻辑】可以用【伪代码】实现如下：

	String path = ...

	if (path.equals("/hello")) {
	    dispatchTo(helloServlet);
	} else if (path.equals("/signin")) {
	    dispatchTo(signinServlet);
	} else {
	    // 所有未匹配的路径均转发到"/"
	    dispatchTo(indexServlet);
	}


所以我们在【浏览器】输入一个【http://localhost:8080/abc】也会看到【未匹配到path而选择的IndexServlet锁生成的页面】。
【因为没有@WebServlet(urlPatterns="/abc") 】

=========================================================================


#	HttpServletRequest

HttpServletRequest封装了一个【HTTP请求】，【HTTP Request】实际上是从【ServletRequest继承而来】。

最早设计Servlet时，设计者希望Servlet不仅能【处理HTTP】，也能【处理类似SMTP等】其他协议，因此，单独抽出了【ServletRequest接口】，但实际上除了【HTTP】外，并【没有其他协议】会用【Servlet处理】，所以这是一个【过度设计】。

我们通过【HttpServletRequest】提供的【接口方法】可以拿到【HTTP请求】的几乎全部信息，【常用的方法】有：

· getMethod()：返回请求方法，例如，"GET"，"POST"；
· getRequestURI()：返回请求路径，但不包括请求参数，例如，"/hello"；
· getQueryString()：返回请求参数，例如，"name=Bob&a=1&b=2"；
· getParameter(name)：返回请求参数，GET请求从URL读取参数，POST请求从Body中读取参数；
· getContentType()：获取请求Body的类型，例如，"application/x-www-form-urlencoded"；
· getContextPath()：获取当前Webapp挂载的路径，对于ROOT来说，总是返回空字符串""；
· getCookies()：返回请求携带的所有Cookie；
· getHeader(name)：获取指定的Header，对Header名称不区分大小写；
· getHeaderNames()：返回所有Header名称；
· getInputStream()：如果该请求带有HTTP Body，该方法将打开一个输入流用于读取Body；
· getReader()：和getInputStream()类似，但打开的是Reader；
· getRemoteAddr()：返回客户端的IP地址；
· getScheme()：返回协议类型，例如，"http"，"https"；

--------------------------

此外，HttpServletRequest还有【两个方法】：
	· setAttribute()
	· getAttribute()

可以给【当前HttpServletRequest对象】附加【多个Key-Value】，
相当于把【HttpServletRequest】当作【一个Map<String, Object>】使用。

调用【HttpServletRequest的方法】时，注意务必阅读【接口方法的文档说明】，因为【有的方法】会返回【null】，例如【getQueryString()的文档】就写了：

... This method returns null if the URL does not have a query string...


=====================================================================


#	HttpServletResponse

HttpServletResponse封装了【一个HTTP响应】。

由于【HTTP响应】必须先【发送Header，再发送Body】，所以，【操作HttpServletResponse对象】时，必须先调用【设置Header的方法】，最后调用【发送Body的方法】。


常用的【设置Header的方法】有：
· setStatus(sc)：设置响应代码，默认是200；
· setContentType(type)：设置Body的类型，例如，"text/html"；
· setCharacterEncoding(charset)：设置字符编码，例如，"UTF-8"；
· setHeader(name, value)：设置一个Header的值；
· addCookie(cookie)：给响应添加一个Cookie；
· addHeader(name, value)：给响应添加一个Header，因为HTTP协议允许有多个相同的Header；


【写入响应】时，需要【通过getOutputStream()】获取【写入流】，或者通过【getWriter()】获取【字符流】，【二者】只能获取【其中一个】。

写入【响应前】,无需设置setContentLength(),
因为【底层服务器】会根据【写入的字节数】自动设置，如果【写入的数据量】很小，实际上会先写入【缓冲区】; 
如果【写入的数据量】很大，服务器会自动采用【Chunked编码】让【浏览器】能识别【数据结束符】而【不需要设置Content-Length头】。
(Chunked编码 = 分块编码)

-----------------------------------------

但是，写入完毕后【调用flush()】却是【必须】的，因为【大部分Web服务器】都【基于HTTP/1.1协议】，会【复用TCP连接】。

如果【没有调用flush()】，将导致【缓冲区的内容】无法及时【发送到客户端】。

此外，【写入完毕后】千万不要【调用.close()方法】，原因同样是因为会【复用TCP连接】，如果【关闭写入流】，将【关闭TCP连接】，使得【Web服务器】无法复用【此TCP连接】。

	PS: 【写入完毕后】对【输出流】调用flush(),而不是close()方法！

--------------------------------------------

有了 HttpServletRequest 和 HttpServletResponse 这【2个高级接口】，我们就不需要【直接处理HTTP协议】。

注意到【具体的实现类】是由【各服务器】提供的，而我们编写的【Web应用程序】只关心【接口方法】，并不需要关心【具体实现的子类】。


=====================================================================


#	Servlet多线程模型

【一个Servlet类】在【服务器】中只有【一个实例】，但对于【每个HTTP请求】，【Web服务器】会使用【多线程执行请求】。

因此，一个Servlet的【doGet()、doPost()等处理请求】的【方法】是【多线程并发执行】的。

如果【Servlet】中定义了【字段】，要注意【多线程"并发访问的问题"】： */
public class HelloServlet extends HttpServlet {

	private Map<String, String> map = new ConcurrentHashMap<>();

	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// 注意: 【读写】ConcurrentHashMap<>() Field map是【多线程并发】的
		this.map.put(key, value);
	}
}


/*
对于【每个请求】，【Web服务器】会创建【唯一的HttpServletRequest和HttpServletResponse实例】，

因此，【HttpServletRequest和HttpServletResponse实例】只有在【当前处理线程】中有效，它们总是【当前线程】的【局部变量】，不存在【多线程共享的问题,也就是非全局变量】。


====================================================================


#	----- Servlet进阶 の 小结 -----


1. 一个Webapp中的【多个Servlet】依靠【路径映射】来处理【不同的请求】；

2. 【映射为/的Servlet】可处理【所有“未匹配”的请求】；

3. 【如何处理请求】取决于【Servlet覆写的对应方法】；
	例如:
		@Override
		protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
			//...
		}

4. 【Web服务器】通过【多线程】处理【HTTP请求】，【一个Servlet的处理方法】可以由【多线程并发】执行。




*/





